package cheme.Interpreter.AST.Arithmetic;

import cheme.Interpreter.AST.Interfaces.IAstComplexExpression;
import cheme.Interpreter.AST.Interfaces.IAstExpression;
import cheme.Interpreter.AST.Interfaces.IAstType;
import cheme.Interpreter.AST.Interfaces.IAstValue;
import cheme.Interpreter.AST.Interfaces.IObserver;
import cheme.Interpreter.AST.Interfaces.ISubject;
import cheme.Interpreter.AST.Interfaces.SubjectCore;

public class AstExpression implements IAstComplexExpression
{
	IAstExpression expression;
	IAstType type;
	
	private ValueationState valueationState =  ValueationState.Unknown;
	
	private SubjectCore subjectCore = new SubjectCore(this);
	
	public AstExpression()
	{
	}
	
	public AstExpression(IAstExpression expression, IAstType type)
	{
		super();
		
		/*
		 * One of the two parameters must be non-null for this expression to be
		 * valid.
		 */
		assert null != expression || null != type;
		
		this.expression = expression;
		this.type = type;
		
		if(null == expression)
		{
			try
			{
				this.expression  = (IAstExpression)IAstValue.implicit.clone();
				//this.valueationState =  ValueationState.Evaluated;
			}
			catch(Exception e)
			{
				throw new Error(
						"AstExpression.AstExpression(IAstExpression, IAstType)" + 
						" could not create an implicit value.");
			}
		}
		
		valueationState = this.expression.getValueationState();
		this.expression.attach(this);
	}
	
	public double evaluate()
	{
		if(null != expression)
		{
			return expression.evaluate();
		}
		return 0;
	}

	/**
	 * @return the operation
	 */
	public IAstExpression getExpression()
	{
		return expression;
	}

	/**
	 * @param expression the operation to set
	 */
	public void setExpression(IAstExpression expression)
	{
		if(null != this.expression)
		{
			this.expression.detach(this);
		}
		
		if(null != expression)
		{
			expression.attach(this);
		}
		
		this.expression = expression;
	}

	/**
	 * @return the type
	 */
	public IAstType getType()
	{
		return type;
	}

	/**
	 * @param type the type to set
	 */
	public void setType(IAstType type)
	{
		this.type = type;
	}
	
	public ValueationState getValueationState()
	{
		return valueationState;
	}

	public void setValueationState(ValueationState valueationState)
	{
		this.valueationState = valueationState;
	}

	@Override
	public String toString()
	{
		if(null != getExpression() && null != getType())
		{
			return getExpression().toString() + " " + getType().toString();
		}
		
		return 
			((null != getExpression()) ? getExpression().toString() : "? ") + 
			((null != getType()) ? getType().toString() : "");
	}

	public boolean isUnknown()
	{
		if(null != getExpression())
		{
			return getExpression().isUnknown();
		}
		
		/*
		 * Is it logical to have incomplete expressions (those with a null 
		 * operand) coinsidered as unknown values? Or should it be an error state?
		 * I am thinking the latter; 
		 */
		return false;
	}
	
	public void notify(ISubject s)
	{
		notifyObservers();
	}
	
	public void attach(IObserver o)
	{
		subjectCore.attach(o);
	}

	public void detach(IObserver o)
	{
		subjectCore.detach(o);
	}

	public void notifyObservers()
	{
		subjectCore.notifyObservers();
	}
}
